<?php

namespace App\Http\Controllers\Api;

use App\Enums\AssetStatus;
use App\Enums\DivideStatus;
use App\Enums\OrderStatus;
use App\Enums\ProductStatus;
use App\Enums\ProductType;
use App\Enums\WalletStatus;
use App\Exceptions\ApiException;
use App\Http\Controllers\Controller;
use App\Models\AgentConfig;
use App\Models\Asset;
use App\Models\AutoLockTask;
use App\Models\FrontendUser;
use App\Models\Order;
use App\Models\Product;
use App\Models\ProductConfig;
use App\Models\ProductToken;
use App\Services\VerifyCodeService;
use App\Services\WalletService;
use Carbon\Carbon;
use Illuminate\Contracts\Cache\LockTimeoutException;
use Illuminate\Support\Arr;

class ResaleController extends Controller
{
    /** @var WalletService */
    private $walletService;

    /** @var VerifyCodeService */
    private $verifyCodeService;

    /**
     * AssetController constructor.
     */
    public function __construct(WalletService $walletService, VerifyCodeService $verifyCodeService)
    {
        $this->walletService = $walletService;
        $this->verifyCodeService = $verifyCodeService;
    }

    /** 查询转售信息 */
    public function prepareResale($assetId)
    {
        /** @var FrontendUser $user */
        $user = auth()->user();
        $asset = Asset::query()->where('user_id', $user->id)
            ->find($assetId);
        $product = Product::query()->where('contract_address', $asset->contract_address)->first();
        // 检查转售配置
        $this->checkResaleConfig($asset, $product);
        $agentConfigs = AgentConfig::query()
            ->where('agent_id', request()->agent_id)
            ->whereIn('config_key', [
                AgentConfig::PAY_CHANNEL,
                AgentConfig::PAY_RESALE_SERVICE_FEE_RATE,   // 平台转售订单手续费率
                AgentConfig::PAY_RESALE_AGENT_FEE_RATE,     // 代理转售订单手续费率
            ])->get()->mapWithKeys(function (AgentConfig $config) {
                return [$config->config_key => $config->config_value];
            })->toArray();
        $payChannel = Arr::get($agentConfigs, AgentConfig::PAY_CHANNEL);
        if ('sdpay' !== $payChannel) {
            throw new ApiException('当前配置的支付通道不支持二级市场交易');
        }
        //检查是否开通钱包
        if ($user->wallet->status->isNot(WalletStatus::OPENED)) {
            throw new ApiException('必须开通钱包才能进行资产转售');
        }

        $serviceFeePercentage = array_sum(Arr::only($agentConfigs, [
            AgentConfig::PAY_RESALE_SERVICE_FEE_RATE,
            AgentConfig::PAY_RESALE_AGENT_FEE_RATE,
        ]));
        [$minResalePrice, $maxResalePrice] = $this->getResalePriceRange($product);

        return [
            'product_name' => $product->name,
            'product_logo_url' => $product->logo_url,
            'min_resale_price' => $minResalePrice,
            'max_resale_price' => $maxResalePrice,
            'service_fee_percentage' => $serviceFeePercentage,
        ];
    }

    /** 发布转售商品 */
    public function publishResaleWithVerifyCode()
    {
        /** @var FrontendUser $user */
        $user = auth()->user();

        request()->validate([
            'order_asset_id' => 'required',
            'resale_price' => 'required',
            'verify_code' => 'required',
        ]);
        $auth = $user->auth;
        $this->verifyCodeService->check($auth->bank_card_mobile, request()->verify_code);

        $this->doPublishResale($user, request()->order_asset_id, request()->resale_price);
    }

    /** 发布转售（使用二级密码） */
    public function publishResaleWithSecondPass()
    {
        request()->validate([
            'order_asset_id' => 'required',
            'resale_price' => 'required',
            'second_pass' => 'required',
        ]);
        /** @var FrontendUser $user */
        $user = auth()->user();
        if (!\Hash::check(request()->second_pass, $user->second_pass)) {
            throw new ApiException('二级密码不正确');
        }
        $this->doPublishResale($user, request()->order_asset_id, request()->resale_price);
    }

    /** 取消转售 */
    public function cancelResale($productId)
    {
        /** @var Product $product */
        $product = $this->getMyResaleProductQuery()->find($productId);
        if (!$product) {
            throw new ApiException('转售商品未找到');
        }
        if ($product->left_count <= 0) {
            throw new ApiException('商品已被锁定，不能取消转售');
        }
        $tokenIds = ProductToken::query()
            ->where('product_id', $productId)
            ->get()->map(function (ProductToken $token) {
                return $token->token_id;
            });
        Asset::query()
            ->where('user_id', auth()->id())
            ->whereIn('token_id', $tokenIds)
            ->where('asset_status', AssetStatus::ON_SALE)
            ->update(['asset_status' => AssetStatus::HOLD]);

        $product->status = ProductStatus::STOP;
        $product->save();

        //指定时间内禁止再次发起转售
        $cancelLockTime = AgentConfig::getConfig(request()->agent_id, AgentConfig::PRODUCT_RESALE_CANCEL_LOCK_TIME, 0);
        if ($cancelLockTime > 0) {
            foreach ($tokenIds as $tokenId) {
                \Cache::set($this->getCancelLockKey($tokenId), Carbon::now()->addMinutes($cancelLockTime), $cancelLockTime * 60);
            }
        }
    }

    /** 查询我的转售商品 */
    public function listMyResales()
    {
        $page = request()->page ?: 1;
        $limit = request()->limit ?: 10;

        $count = $this->getMyResaleProductQuery()->count();
        $list = $this->getMyResaleProductQuery()
            ->offset(($page - 1) * $limit)->limit($limit)
            ->orderByDesc('id')
            ->get()->map(function (Product $product) {
                return [
                    'id' => $product->id,
                    'product_name' => $product->name,
                    'token_ids' => $product->available_tokens->map(function (ProductToken $token) {
                        return Asset::where('token_id', $token->token_id)
                            ->where('user_id', auth()->id())->value('cert_sn');
                    }),
                    'product_logo_url' => $product->logo_url,
                    'sale_price' => $product->display_price,
                ];
            });

        return [
            'count' => $count,
            'list' => $list,
        ];
    }

    /** 查询转售商品列表 */
    public function listResaleProducts()
    {
        $page = request()->page ?: 1;
        $limit = request()->limit ?: 10;

        $groupColumns = ['contract_address', 'logo_url', 'name', 'issuer'];
        $resaleSummary = $this->getResaleProductQuery()
            ->select(array_merge($groupColumns, [
                \DB::raw('max(price) as max_price'),
                \DB::raw('min(price) as min_price'),
                \DB::raw('count(*) as resale_count'),
            ]))
            ->groupBy($groupColumns)
            ->get()
            ->mapWithKeys(function (Product $product) {
                return [
                    $product->contract_address => [
                        'max_price' => toYuan($product->max_price),
                        'min_price' => toYuan($product->min_price),
                        'resale_count' => $product->resale_count,
                    ],
                ];
            })->toArray();

        $list = [];
        $resalableProducts = Product::query()
            ->with(['configs', 'category'])
            ->where('agent_id', request()->agent_id)
            ->where('type', ProductType::ISSUE)
            ->when(request()->keyword, function ($q) {
                return $q->where('name', 'like', '%' . request()->keyword . '%');
            })
            ->when(request()->category_id, function ($q) {
                return $q->where('category_id', request()->category_id);
            })
            ->orderByDesc('id')
            ->get();

        foreach ($resalableProducts as $product) {
            $forbidResale = true;
            foreach ($product->configs as $config) {
                if (ProductConfig::RESALE_FORBID == $config->config_key) {
                    $forbidResale = 1 == $config->config_value;
                }
            }
            if (!$forbidResale) {
                $category = $product->category;
                $list[] = array_merge([
                    'name' => $product->name,
                    'logo_url' => $product->logo_url,
                    'category' => $category ? $category->name : '',
                    'contract_address' => $product->contract_address,
                    'issuer' => $product->issuer,
                ], Arr::get($resaleSummary, $product->contract_address, [
                    'resale_count' => 0,
                ]));
            }
        }
        $sortCols = array_column($list, 'resale_count');
        //按在售数量排序
        array_multisort($sortCols, SORT_DESC, $list);

        return [
            'count' => count($list),
            'list' => array_slice($list, ($page - 1) * $limit, $limit),
        ];
    }

    /** 根据商品标识查询转售存证列表 */
    public function listResaleTokens()
    {
        request()->validate([
            'contract_address' => 'required',
        ]);
        $contractAddress = request()->contract_address;
        $query = Product::query()->where('contract_address', $contractAddress);
        $product = $query->first();
        $page = request()->page ?: 1;
        $limit = request()->limit ?: 10;
        $resaleQuery = $this->getResaleProductQuery()->where('contract_address', $contractAddress);
        $count = $resaleQuery->count();
        $priceRange = $this->getResaleProductQuery()->where('contract_address', $contractAddress)
            ->select([
                \DB::raw('max(price) as max_price'),
                \DB::raw('min(price) as min_price'),
            ])->first();
        $list = $resaleQuery
            ->offset($limit * ($page - 1))
            ->limit($limit)
            ->orderBy('price')
            ->get()
            ->map(function (Product $product) {
                return [
                    'resale_product_id' => $product->id,
                    'holder' => hide_mobile($product->resale_user->mobile),
                    'price' => $product->display_price,
                    'is_locked' => $product->left_count <= 0,
                ];
            });

        return [
            'count' => $count,
            'product' => [
                'name' => $product->name,
                'logo_url' => $product->logo_url,
                'category' => $product->category ? $product->category->name : '',
                'max_price' => toYuan(Arr::get($priceRange, 'max_price', 0)),
                'min_price' => toYuan(Arr::get($priceRange, 'min_price', 0)),
                'issuer' => $product->issuer,
            ],
            'list' => $list,
        ];
    }

    /** 查询转售订单 */
    public function listResaleOrders()
    {
        $page = request()->page ?: 1;
        $limit = request()->limit ?: 10;

        $query = Order::query()
            ->whereIn('order_status', [OrderStatus::PAID, OrderStatus::NOT_PAY])
            ->whereHas('product', function ($q) {
                return $q->where('resale_user_id', auth()->id());
            });

        $count = $query->count();
        $list = $query
            ->offset(($page - 1) * $limit)->limit($limit)
            ->orderByDesc('id')
            ->get()->map(function (Order $order) {
                return [
                    'order_id' => $order->id,
                    'order_no' => $order->order_no,
                    'product_name' => $order->product_name,
                    'product_logo_url' => $order->product_logo_url,
                    'quantity' => $order->quantity,
                    'price' => $order->display_sale_price,
                    'buyer_mobile' => $order->user->mobile,
                    'create_time' => $order->created_at->toDateTimeString(),
                    'status' => $order->order_status->value,
                ];
            });

        return [
            'count' => $count,
            'list' => $list,
        ];
    }

    public function getResaleOrderDetail($orderId)
    {
        /** @var FrontendUser $user */
        $user = auth()->user();

        $order = Order::query()
            ->whereHas('product', function ($q) {
                return $q->where('resale_user_id', auth()->id());
            })->find($orderId);
        if (!$order) {
            throw new ApiException('无效的转售订单');
        }

        $arr = [
            'id' => $order->id,
            'product_id' => $order->product_id,
            'order_no' => $order->order_no,
            'product_name' => $order->product_name,
            'product_type' => $order->product->type->value,
            'product_logo_url' => $order->product_logo_url,
            'sale_price' => $order->display_sale_price,
            'quantity' => $order->quantity,
            'order_amount' => $order->display_order_amount,
            'status' => $order->order_status->value,
            'create_time' => $order->created_at->toDateTimeString(),
        ];
        $arr['buyer_mobile'] = $order->user->mobile;
        // 手续费
        $agentConfigs = AgentConfig::query()
            ->where('agent_id', request()->agent_id)
            ->whereIn('config_key', [
                AgentConfig::PAY_RESALE_SERVICE_FEE_RATE,   // 平台转售订单手续费率
                AgentConfig::PAY_RESALE_AGENT_FEE_RATE,     // 代理转售订单手续费率
                AgentConfig::PRODUCT_RESALE_MAX_PRICE,      // 转售最高价格
            ])->get()->mapWithKeys(function (AgentConfig $config) {
                return [$config->config_key => $config->config_value];
            })->toArray();
        $arr['service_fee_percentage'] = array_sum(Arr::only($agentConfigs, [
            AgentConfig::PAY_RESALE_SERVICE_FEE_RATE,
            AgentConfig::PAY_RESALE_AGENT_FEE_RATE,
        ]));
        // 收款账号
        $auth = $user->auth;
        $arr['bank_name'] = $auth->bank->name;
        $arr['bank_card_no'] = $this->hideBankCardNo($auth->bank_card_no);

        // 未支付订单状态
        if ($order->order_status->is(OrderStatus::NOT_PAY())) {
            $arr['auto_cancel_time'] = $order->auto_cancel_time;
        }
        // 已支付订单状态
        if ($order->order_status->is(OrderStatus::PAID)) {
            $payment = $order->payment;
            $arr = array_merge($arr, [
                'pay_time' => $payment->pay_time,
                'pay_way' => $payment->pay_way->value,
                'trans_status' => $order->trans_status->value,
                'trans_failed_reason' => $order->trans_fail_reason,
                'divide_status' => $payment->divide_status->value,
            ]);
            if ($payment->divide_status->is(DivideStatus::DIVIDED())) {
                $arr['user_receive_amount'] = toYuan($payment->user_receive_amount);
            }
        }

        return $arr;
    }

    private function getResalePriceRange(Product $product)
    {
        $max = -1;
        $min = 0;
        $resaleConfigs = $product->configs()
            ->whereIn('config_key', [
                ProductConfig::RESALE_MAX_PRICE,
                ProductConfig::RESALE_PRICE_INCR_LIMIT,
                ProductConfig::RESALE_PRICE_DECR_LIMIT,
                ProductConfig::RESALE_INIT_BASE_PRICE,
                ProductConfig::RESALE_BASE_PRICE,
            ])
            ->get()
            ->mapWithKeys(function (ProductConfig $config) {
                return [$config->config_key => $config->config_value];
            });
        $maxResalePrice = Arr::get($resaleConfigs, ProductConfig::RESALE_MAX_PRICE);
        if ($maxResalePrice && $maxResalePrice > 0) {
            $max = $maxResalePrice;
        }
        $basePrice = Arr::get($resaleConfigs, ProductConfig::RESALE_BASE_PRICE);
        if (!$basePrice) {
            $basePrice = Arr::get($resaleConfigs, ProductConfig::RESALE_INIT_BASE_PRICE, 0);
        }
        $priceIncrLimit = Arr::get($resaleConfigs, ProductConfig::RESALE_PRICE_INCR_LIMIT);
        if ($priceIncrLimit && $priceIncrLimit > 0) {
            $upperLimit = $basePrice * (100 + $priceIncrLimit) / 100;
            if ($max > 0) {
                $max = min($max, $upperLimit);
            } else {
                $max = $upperLimit;
            }
        }
        $priceDecrLimit = Arr::get($resaleConfigs, ProductConfig::RESALE_PRICE_DECR_LIMIT);
        if ($priceDecrLimit && $priceDecrLimit > 0) {
            $min = max(0, $basePrice * (100 - $priceDecrLimit) / 100);
        }

        return [number_format($min, 2, '.', ''), number_format($max, 2, '.', '')];
    }

    private function getCancelLockKey($tokenId)
    {
        return 'resale_cancel_lock_' . $tokenId;
    }

    private function doPublishResale(FrontendUser $user, $assetId, $resalePrice)
    {
        if (1 == $user->punish) {
            throw new ApiException('您已经被限制交易，解除时间' . $user->punish_log->end_time);
        }

        $asset = Asset::query()
            ->where('user_id', $user->id)
            ->whereIn('asset_status', [AssetStatus::HOLD])
            ->find($assetId);
        if (!$asset) {
            throw new ApiException('资产未找到或不可转售');
        }
        $product = Product::query()->where('contract_address', $asset->contract_address)->first();
        // 检查是否被锁定
        if (\Cache::has($this->getCancelLockKey($asset->token_id))) {
            $lockUntil = \Cache::get($this->getCancelLockKey($asset->token_id));
            $minutesLeft = Carbon::parse($lockUntil)->diffInMinutes(Carbon::now());

            throw new ApiException('转售冷却中，请' . $minutesLeft . '分钟后再试');
        }
        // 检查转售配置
        $this->checkResaleConfig($asset, $product);
        // 转售价格限制
        [$minPrice, $maxPrice] = $this->getResalePriceRange($product);
        if ($resalePrice < $minPrice) {
            throw new ApiException('转售价格低于最低价限制');
        }
        if ($maxPrice > 0 && $resalePrice > $maxPrice) {
            throw new ApiException('转售价格高于最高价限制');
        }
        $lock = \Cache::lock('asset_resale_' . $assetId, 5);

        try {
            $lock->block(1); // 如果获取不到锁，则等待2秒
            \DB::transaction(function () use ($product, $asset, $resalePrice) {
                $price = toFen($resalePrice);

                // 创建转售商品
                $resaleProduct = new Product();
                $resaleProduct->copyFrom($product);
                $resaleProduct->type = ProductType::RESALE();
                $resaleProduct->resale_user_id = auth()->id();
                $resaleProduct->issue_count = 1;
                $resaleProduct->left_count = 1;
                $resaleProduct->sales_count = 0;
                $resaleProduct->price = $price;
                $resaleProduct->online_time = now();
                $resaleProduct->status = ProductStatus::ON_SALE();

                // 检查是否满足自动锁单要求
                $autoLockTask = AutoLockTask::query()
                    ->where('agent_id', request()->agent_id)
                    ->where('contract_address', $product->contract_address)
                    ->where('start_time', '<=', now())
                    ->where('end_time', '>=', now())
                    ->where('min_price', '<=', $price)
                    ->where('max_price', '>=', $price)
                    ->first();

                if ($autoLockTask) {
                    $resaleProduct->left_count = 0;
                    $resaleProduct->sales_count = 1;
                }
                $resaleProduct->save();
                // 创建转售商品的资产凭证
                $resaleToken = new ProductToken();
                $resaleToken->product_id = $resaleProduct->id;
                $resaleToken->token_id = $asset->token_id;
                $resaleToken->save();
                if ($autoLockTask) {
                    // 锁单
                    $order = new Order();
                    $order->agent_id = request()->agent_id;
                    $order->user_id = $autoLockTask->locker_user_id;
                    $order->order_no = Order::generateOrderNo($resaleProduct->type);
                    $order->product_id = $resaleProduct->id;
                    $order->product_name = $resaleProduct->name;
                    $order->product_logo_url = $resaleProduct->logo_url;
                    $order->quantity = 1;
                    $order->sale_price = $resaleProduct->price;
                    $order->order_amount = $resaleProduct->price;
                    $order->is_resale = true;
                    $order->manual_lock_until = now()->addMinutes($autoLockTask->lock_minutes);
                    $order->order_status = OrderStatus::NOT_PAY;
                    $order->save();
                    $autoLockTask->increment('locked_count');
                }

                // 原资产标记为转售中
                $asset->asset_status = AssetStatus::ON_SALE();
                $asset->save();
            });
        } catch (LockTimeoutException $e) {
            throw new ApiException('请不要重复请求');
        } finally {
            $lock->release();
        }
    }

    private function hideBankCardNo($bankCardNo)
    {
        return substr($bankCardNo, 0, 4) . '********' . substr($bankCardNo, -4);
    }

    /** 查询正在销售的转售商品 */
    private function getResaleProductQuery()
    {
        return Product::query()
            ->where('agent_id', request()->agent_id)
            ->where('type', ProductType::RESALE)
            ->where('status', ProductStatus::ON_SALE);
    }

    /** 查询我的转售商品 */
    private function getMyResaleProductQuery()
    {
        return $this->getResaleProductQuery()
            ->where('resale_user_id', auth()->id());
    }

    /** 检查转售配置 */
    private function checkResaleConfig(Asset $asset, Product $product)
    {
        // 判断商品转售配置
        $resaleConfigs = $product->configs()
            ->whereIn('config_key', [ProductConfig::RESALE_FORBID, ProductConfig::RESALE_DURATION, ProductConfig::IS_LOCKED])
            ->get()
            ->mapWithKeys(function (ProductConfig $config) {
                return [$config->config_key => $config->config_value];
            });
        $resaleForbid = (bool) Arr::get($resaleConfigs, ProductConfig::RESALE_FORBID, true);
        $resaleDuration = Arr::get($resaleConfigs, ProductConfig::RESALE_DURATION, 0);
        $isLocked = (bool) Arr::get($resaleConfigs, ProductConfig::IS_LOCKED, false);
        if ($isLocked) {
            throw new ApiException('资产已锁定，不可转售');
        }
        if ($resaleForbid) {
            throw new ApiException('该资产不支持转售');
        }
        if ($resaleDuration > 0 && now()->lt($asset->created_at->addDays($resaleDuration))) {
            throw new ApiException("资产持有超过{$resaleDuration}天后才可以转售");
        }
    }
}
